package br.edu.ufcg.unknown.lexico;

import br.edu.ufcg.unknown.sintatico.IdentificaTK;

/**
 * Representa um Token lido a partir do fluxo de caractere. Criado pelo
 * Analisador Léxico e utilizado como entrada para o Analisador Sintático.
 * 
 * @author Fábio Jorge
 * @author Jaindson Valentim
 * @author Moisés Rodrigues
 */
public class Token implements Cloneable {

	public static String LINE_SEPARATOR = System.getProperty("line.separator");

	/**
	 * Linha onde o Token apareceu.
	 */
	private int linha;

	/**
	 * Coluna da Linha onde o Token apareceu.
	 */
	private int coluna;

	/**
	 * Lexema do Token. Sequencia de caracteres que formam o token.
	 */
	private String lexema;

	/**
	 * Código que representa este token.
	 */
	private int codigo;
	
	private IdentificaTK ident = new IdentificaTK();;

	public static final String TIPO_T_IDENT = "t_ident";
	public static final String TIPO_T_OPERADOR = "t_operador";
	public static final String TIPO_T_PARENTISADOR = "t_parentisador";
	public static final String TIPO_T_SINAL_PONTUACAO = "t_sinal_pontuacao";
	public static final String TIPO_T_NUMERO = "t_numero";
	public static final String TIPO_T_CADEIA = "t_cadeia";
	
	/**
	 * Tipo do token. Pode assumir os valores:
	 * 
	 * <ul>
	 * <li>t_ident</li>
	 * <li>t_operador</li>
	 * <li>t_parentisador</li>
	 * <li>t_sinal_pontuacao</li>
	 * <li>t_numero</li>
	 * <li>t_cadeia</li>
	 * </ul>
	 */
	private String tipo;

	/**
	 * Nome do token. Pode assumir os valores:
	 * 
	 * <ul>
	 * <li>t_abre_cha</li>
	 * <li>t_abre_colc</li>
	 * <li>t_abre_par</li>
	 * <li>t_and</li>
	 * <li>t_array</li>
	 * <li>t_atrib</li>
	 * <li>t_begin</li>
	 * <li>t_boolean</li>
	 * <li>t_const</li>
	 * <li>t_dif</li>
	 * <li>t_div</li>
	 * <li>t_divisao</li>
	 * <li>t_do</li>
	 * <li>t_dois_pts</li>
	 * <li>t_else</li>
	 * <li>t_end</li>
	 * <li>t_false</li>
	 * <li>t_fecha_cha</li>
	 * <li>t_fecha_colc</li>
	 * <li>t_fecha_par</li>
	 * <li>t_if</li>
	 * <li>t_igual</li>
	 * <li>t_integer</li>
	 * <li>t_maior</li>
	 * <li>t_maior_ig</li>
	 * <li>t_mais</li>
	 * <li>t_menor</li>
	 * <li>t_menor_ig</li>
	 * <li>t_menos</li>
	 * <li>t_mod</li>
	 * <li>t_not</li>
	 * <li>t_of</li>
	 * <li>t_or</li>
	 * <li>t_program</li>
	 * <li>t_pto_vgl</li>
	 * <li>t_readln</li>
	 * <li>t_string</li>
	 * <li>t_subprogram</li>
	 * <li>t_subprograms</li>
	 * <li>t_then</li>
	 * <li>t_true</li>
	 * <li>t_var</li>
	 * <li>t_vezes</li>
	 * <li>t_while</li>
	 * <li>t_write</li>
	 * </ul>
	 * 
	 */
	private String nome;

	/**
	 * True se for uma palavra reservada da linguagem.
	 */
	private boolean palavraReservada;

	private Token() {
		super();
	}

	public Token(String lexema, String tipo) {
		super();
		this.lexema = lexema;
		this.tipo = tipo;
	}

	public Token(String lexema, String tipo, String nome, boolean palavraReservada) {
		this(lexema, tipo);
		this.nome = nome;
		this.palavraReservada = palavraReservada;
	}

	public Token(String lexema, String tipo, String nome, boolean palavraReservada, int linha) {
		this(lexema, tipo, nome, palavraReservada);
		this.linha = linha;
	}
	
	public Token(String lexema, String tipo, String nome, boolean palavraReservada, int linha, int coluna) {
		this(lexema, tipo, nome, palavraReservada, linha);
		this.coluna = coluna;
	}

	public String getLexema() {
		return lexema;
	}

	public int getCodigo() {
		return codigo;
	}

	public void setCodigo(int codigo) {
		this.codigo = codigo;
	}

	public String getTipo() {
		return tipo;
	}

	public String getNome() {
		return nome;
	}
	
	public void setNome(String nome) {
		this.nome = nome;
	}

	public boolean ehPalavraReservada() {
		return palavraReservada;
	}
	
	public void setPalavraReservada(boolean palavraReservada) {
		this.palavraReservada = palavraReservada;
	}

	public boolean ehIdentificador() {
		if (this.tipo == null) {
			return false;
		}
		return this.tipo.equals("t_ident");
	}

	public boolean ehCadeia() {
		if (this.tipo == null) {
			return false;
		}
		return this.tipo.equals("t_cadeia");
	}

	public boolean ehNumero() {
		if (this.tipo == null) {
			return false;
		}
		return this.tipo.equals("t_numero");
	}

	public boolean ehOperador() {
		if (this.tipo == null) {
			return false;
		}
		return this.tipo.equals("t_operador");
	}
	
	public boolean ehOperadorAdicao() {
		if (this.nome == null) {
			return false;
		}
		return this.nome.equals("t_mais");
	}
	
	public boolean ehOperadorSubtracao() {
		if (this.nome == null) {
			return false;
		}
		return this.nome.equals("t_menos");
	}

	public boolean ehParentisador() {
		if (this.tipo == null) {
			return false;
		}
		return this.tipo.equals("t_parentisador");
	}

	public boolean ehSinalDePontuacao() {
		if (this.tipo == null) {
			return false;
		}
		return this.tipo.equals("t_sinal_pontuacao");
	}

	public int getLinha() {
		return linha;
	}

	public int getColuna() {
		return coluna;
	}

	public boolean equals(Object obj) {
		Token token = (Token) obj;

		if (this.codigo != token.codigo) {
			return false;
		}

		if (this.lexema != null && token.lexema != null) {
			if (!this.lexema.equals(token.lexema)) {
				return false;
			}
		} else if (this.lexema != null || token.lexema != null) {
			return false;
		}

		if (this.nome != null && token.nome != null) {
			if (!this.nome.equals(token.nome)) {
				return false;
			}
		} else if (this.nome != null || token.nome != null) {
			return false;
		}

		if (this.palavraReservada != token.palavraReservada) {
			return false;
		}

		if (this.tipo != null && token.tipo != null) {
			if (!this.tipo.equals(token.tipo)) {
				return false;
			}
		} else if (this.tipo != null || token.tipo != null) {
			return false;
		}

		if (this.linha != token.linha) {
			return false;
		}

		if (this.coluna != token.coluna) {
			return false;
		}

		return true;
	}

	public int hashCode() {
		return this.toString().hashCode();
	}

	public String toString() {
		StringBuilder sb = new StringBuilder();

		sb.append(this.codigo);
		sb.append(LINE_SEPARATOR);
		sb.append(this.palavraReservada);
		sb.append(LINE_SEPARATOR);
		sb.append(this.lexema);
		sb.append(LINE_SEPARATOR);
		sb.append(this.nome);
		sb.append(LINE_SEPARATOR);
		sb.append(this.tipo);
		sb.append(LINE_SEPARATOR);
		sb.append(this.linha);
		sb.append(LINE_SEPARATOR);
		sb.append(this.coluna);

		return sb.toString();
	}

	public Token clone() {

		Token clone = new Token();
		clone.codigo = this.codigo;
		clone.palavraReservada = this.palavraReservada;
		clone.lexema = this.lexema;
		clone.nome = this.nome;
		clone.tipo = this.tipo;
		clone.linha = this.linha;
		clone.coluna = this.coluna;

		return clone;
	}

	public void setLinha(int linha) {
		this.linha = linha;
	}

	public void setColuna(int coluna) {
		this.coluna = coluna;
	}

	public void setLexema(String string) {
		this.lexema = string;
		
	}

	public boolean ehTerminal() {
		return (this.ehCadeia() || this.ident.eh_STRING(this)) || (this.ehNumero() || this.ident.eh_INTEGER(this)) || ( this.ehIdentificador() && !this.ehPalavraReservada())
				|| (this.ident.eh_TRUE(this) || this.ident.eh_FALSE(this) || this.ident.eh_BOOL(this));
	}

	public boolean ehOperadorSemantico() {
		return this.ident.eh_AND(this) || this.ident.eh_OR(this) || this.ident.eh_NOT(this)
				|| this.ident.eh_MULTIPLICACAO(this) || this.ident.eh_DIVISAO(this) || this.ident.eh_DIV(this)
				|| this.ident.eh_MOD(this) 
				|| this.ident.eh_MENOS(this) || this.ident.eh_MAIS(this)
				|| this.ident.eh_IGUAL(this) || this.ident.eh_DIFERENTE(this)
				|| this.ident.eh_MAIOR(this) || this.ident.eh_MENOR(this)
				|| this.ident.eh_MAIOR_IGUAL(this) || this.ident.eh_MENOR_IGUAL(this);
	}
	
	public boolean ehOperadorRelacional() {
		return  this.ident.eh_IGUAL(this) 
				|| this.ident.eh_MAIOR(this) || this.ident.eh_MENOR(this)
				|| this.ident.eh_MAIOR_IGUAL(this) || this.ident.eh_MENOR_IGUAL(this);
	}
	
	public boolean ehOperadorLogico() {
		return  this.ident.eh_AND(this) || this.ident.eh_OR(this) || this.ident.eh_NOT(this);
	}

	public boolean ehOperadorDivisao() {
		if (this.nome == null) {
			return false;
		}
		return this.nome.equals("t_divisao") || this.nome.equals("t_mod") || this.nome.equals("t_div");
	}

	public boolean ehOperadorMultiplicacao() {
		if (this.nome == null) {
			return false;
		}
		return this.nome.equals("t_vezes");
	}

}
